package com.sangmo.fts.jpa;

import com.sangmo.boot.framework.common.session.UserSession;
import com.sangmo.boot.framework.common.utils.ApplicationUtils;
import com.sangmo.boot.framework.common.utils.UuidUtils;
import com.sangmo.boot.framework.data.entity.BaseEntity;
import com.sangmo.boot.framework.data.entity.SoftDelete;
import com.sangmo.boot.framework.event.IEventDispatcher;
import com.sangmo.fts.basic.build.AuditAction;
import com.sangmo.fts.basic.build.EntityAuditActionBuilder;
import com.sangmo.fts.basic.model.AccessLog;
import com.sangmo.fts.basic.model.AuditLog;
import org.apache.commons.lang3.ArrayUtils;
import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class EntityInterceptor extends EmptyInterceptor {

    public final static String[] SKIP = new String[] {AccessLog.class.getSimpleName(), AuditLog.class.getSimpleName()};

    @Override
    public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        super.onDelete(entity, id, state, propertyNames, types);

        if (!ArrayUtils.contains(SKIP, entity.getClass().getSimpleName())) {
            AuditAction a = EntityAuditActionBuilder.newInstance()
                    .setType("DELETE")
                    .setRefEntity((BaseEntity) entity)
                    .buildAction(entity);
            this.dispatchAuditAction(a);
        }
    }

    @Override
    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        boolean ret = audit(true, state, propertyNames, true, true);

        if (!ArrayUtils.contains(SKIP, entity.getClass().getSimpleName())) {
            AuditAction a = EntityAuditActionBuilder.newInstance()
                    .setType("CREATE")
                    .setRefEntity((BaseEntity) entity)
                    .buildAction(entity);
            this.dispatchAuditAction(a);
        }
        return ret;
    }

    @Override
    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
        boolean ret = audit(false, currentState, propertyNames, true, true);

        if (!ArrayUtils.contains(SKIP, entity.getClass().getSimpleName())) {
            AuditAction a = EntityAuditActionBuilder.newInstance()
                    .setType("UPDATE")
                    .setRefEntity((BaseEntity) entity)
                    .buildUpdateAction(getMap(propertyNames, previousState), getMap(propertyNames, currentState));
            this.dispatchAuditAction(a);
        }

        if (entity instanceof SoftDelete) {
            for (int i = 0; i < propertyNames.length; i++) {
                if (SoftDelete.FIELD.equals(propertyNames[i])) {
//                    populateDelete(entity, currentState[i], previousState[i]);
                    break;
                }
            }
        }
        return ret;
    }

    /*private void populateDelete(Object entity, Object currentState, Object previousState) {
        if (!currentState.equals(previousState) &&
                previousState.equals(0L)) {
            AuditAction a = EntityAuditActionBuilder.newInstance()
                    .setType("DELETE")
                    .setRefEntity((BaseEntity) entity)
                    .buildAction(entity);
            this.dispatchAuditAction(a);

            RecycleBinService recycleBinService = ApplicationUtils.getBean(RecycleBinService.class);
            RecycleBinCreation recycleBinCreation = new RecycleBinCreation();
            recycleBinCreation.setEntity(entity);
            recycleBinService.create(recycleBinCreation);
        }
    }*/

    private void dispatchAuditAction(AuditAction action) {
        IEventDispatcher dispatcher = ApplicationUtils.getBean(IEventDispatcher.class);
        dispatcher.dispatch(action.createEvent());
    }

    private Map<String, Object> getMap(String[] names, Object[] state) {
        Map<String, Object> map = new HashMap<>();
        for (int i = 0; i < names.length; i++) {
            map.put(names[i], state[i]);
        }
        return map;
    }


    private boolean audit(boolean isNew, Object[] currentState, String[] propertyNames, boolean autable, boolean uuid) {
        int update = 0;
        Date current = Calendar.getInstance().getTime();
        Object userId = UserSession.getAuthorize().getUserId();

        for (int i = 0; i < propertyNames.length; i++) {
            if (autable) {
                if (isNew) {
                    if ("createdTime".equals(propertyNames[i])) {
                        if (null == currentState[i]) {
                            currentState[i] = current;
                        }
                        update++;
                        continue;
                    } else if ("createdBy".equals(propertyNames[i])) {
                        if (UserSession.getAuthorize() != null) {
                            if (null == currentState[i]) {
                                currentState[i] = userId;
                            }
                            update++;
                        }
                        continue;
                    }
                }
                if ("updatedTime".equals(propertyNames[i])) {
                    if (null == currentState[i]) {
                        currentState[i] = current;
                    }
                    update++;
                    continue;
                } else if ("updatedBy".equals(propertyNames[i])) {
                    if (UserSession.getAuthorize() != null) {
                        if (null == currentState[i]) {
                            currentState[i] = userId;
                        }
                        update++;
                    }
                    continue;
                }
            }

            if (uuid && isNew) {
                if ("uuid".equals(propertyNames[i])) {
                    if (null == currentState[i]) {
                        currentState[i] = UuidUtils.getUUID();
                        update++;
                    }
                    continue;
                }
            }
        }
        return update > 0;
    }
}
